package com.tiancheng.onecode.service.impl;

import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tiancheng.onecode.entity.*;
import com.tiancheng.onecode.enums.BillTypeEnums;
import com.tiancheng.onecode.enums.OrderStatusEnum;
import com.tiancheng.onecode.mapper.BillMapper;
import com.tiancheng.onecode.model.dto.CumulativeGainDTO;
import com.tiancheng.onecode.model.request.admin.QueryBillReq;
import com.tiancheng.onecode.model.request.admin.StatisticsMerchantOrderReq;
import com.tiancheng.onecode.model.response.PageInfo;
import com.tiancheng.onecode.model.response.admin.BillQueryRes;
import com.tiancheng.onecode.model.response.admin.StatisticsMerchantOrderPayRes;
import com.tiancheng.onecode.model.response.admin.TradeStatisticsRes;
import com.tiancheng.onecode.service.*;
import com.tiancheng.onecode.trade.enums.MessageTypeEnum;
import com.tiancheng.onecode.trade.enums.OrderPayStatusEnum;
import com.tiancheng.onecode.trade.enums.SettleStatusEnum;
import com.tiancheng.onecode.trade.enums.VerificationStatusEnum;
import com.tiancheng.onecode.trade.model.SettlementRuleResVO;
import com.tiancheng.onecode.trade.notify.CallbackSettleMsgBO;
import com.tiancheng.onecode.trade.utils.OrderIdHelper;
import com.tiancheng.onecode.utils.ClearingComputeUtils;
import com.tiancheng.onecode.utils.cache.SettleRulesCache;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @Author: kellen
 * @Description:
 * @Date: create in 2024/12/17 15:45
 */
@Slf4j
@Service
public class IBillServiceImpl extends ServiceImpl<BillMapper, Bill> implements IBillService {

    @Resource
    private BillDetailService billDetailService;
    @Resource
    private OrderIdHelper orderIdHelper;
    @Resource
    private SettleRulesCache settleRulesCache;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private IMemberSubOrderService subOrderService;
    @Resource
    private IPlatformMerchantSettleRuleService merchantSettleRuleService;
    @Resource
    private IMemberOrderService orderService;

    private IMemberOrderService getOrderService() {
        return SpringUtil.getBean(IMemberOrderService.class);
    }

    private IMemberOrderRefundService getOrderRefundService() {
        return SpringUtil.getBean(IMemberOrderRefundService.class);
    }

    @Override
    public Boolean createAfterPay(String orderId) {
        final MemberOrder order = getOrderService().queryByOrderId(orderId);
        if (Objects.isNull(order)) {
            log.error("未查询到主订单！订单号：{}", orderId);
            return false;
        }
        if (!order.getPayStatus().equals(OrderPayStatusEnum.paid.getCode())) {
            log.warn("订单支付状态异常！订单号：{}，支付状态：{}", orderId, order.getPayStatus());
            return false;
        }
        final List<MemberSubOrder> subOrders = subOrderService.queryByOrderId(orderId);
        if (CollectionUtils.isEmpty(subOrders)) {
            log.error("未查询到子订单！订单号：{}", orderId);
            return false;
        }
        final List<Bill> bills = this.list(new LambdaQueryWrapper<Bill>().in(Bill::getSubOrderId, subOrders.stream().map(MemberSubOrder::getSubOrderId).collect(Collectors.toList())));
        if (!CollectionUtils.isEmpty(bills)) {
            return true;
        }
        final List<Bill> billList = new ArrayList<>();
        final List<BillDetail> billDetailList = new ArrayList<>();
        for (MemberSubOrder subOrder : subOrders) {
            // 子订单处理
            final Bill bill = new Bill();
            bill.setBillNo(orderIdHelper.getBillNo(subOrder.getSubOrderId()));
            bill.setOrderId(subOrder.getOrderId());
            bill.setSubOrderId(subOrder.getSubOrderId());
            bill.setSubFlowId(subOrder.getSubFlowId());
            bill.setTotalAmount(Objects.nonNull(subOrder.getSubOrderAmount()) ? subOrder.getSubOrderAmount() : 0);
            bill.setPayAmount(Objects.nonNull(subOrder.getPayAmount()) ? subOrder.getPayAmount() : 0);
            bill.setDiscount(Objects.nonNull(subOrder.getDiscount()) ? subOrder.getDiscount() : 0);
            bill.setRefundAmount(0);
            bill.setRefundDiscountAmount(0);
            bill.setSettleStatus(SettleStatusEnum.WAIT.getCode());
            bill.setVerificationStatus(VerificationStatusEnum.WAIT.getCode());
            bill.setBillRemark("");
            bill.setCreatedDt(LocalDateTime.now());
            // 获取清分规则
            final SettlementRuleResVO ruleByMerchantCode = settleRulesCache.getRuleByPlatformMerchantId(subOrder.getPlatformMerchantId());
            // 明细项
            // 1、实付部分
            final BillDetail detail = new BillDetail();
            detail.setBillNo(bill.getBillNo());
            detail.setFlowId(subOrder.getSubFlowId());
            detail.setChannelId(subOrder.getSubFlowId());
            detail.setBillType(BillTypeEnums.pay_pre.getCode());
            detail.setTotalAmount(Objects.nonNull(subOrder.getPayAmount()) ? subOrder.getPayAmount() : 0);
            detail.setAmount(Objects.nonNull(subOrder.getPayAmount()) ? subOrder.getPayAmount() : 0);
            detail.setIsSettle(1);
            detail.setSettleAmount(Objects.nonNull(subOrder.getPayAmount()) ? subOrder.getPayAmount() : 0);
            detail.setCommissionAmount(ClearingComputeUtils.platformCommission(subOrder.getPayAmount(), ruleByMerchantCode));
            detail.setMerchantAmount(detail.getSettleAmount() - detail.getCommissionAmount());
            detail.setPayStatus("SC");
            detail.setPayWay(order.getPayWay());
            detail.setCreatedDt(LocalDateTime.now());
            billDetailList.add(detail);
            if (Objects.nonNull(subOrder.getIsCoupon()) && subOrder.getIsCoupon().equals(1) && Objects.nonNull(subOrder.getDiscount()) && subOrder.getDiscount() > 0) {
                // 2、优惠部分
                final BillDetail discountDetail = new BillDetail();
                discountDetail.setBillNo(bill.getBillNo());
                discountDetail.setFlowId(subOrder.getSubFlowId());
                discountDetail.setChannelId(subOrder.getSubFlowId());
                discountDetail.setBillType(BillTypeEnums.pay_channel_discount.getCode());
                discountDetail.setTotalAmount(Objects.nonNull(subOrder.getDiscount()) ? subOrder.getDiscount() : 0);
                discountDetail.setAmount(Objects.nonNull(subOrder.getDiscount()) ? subOrder.getDiscount() : 0);
                discountDetail.setIsSettle(1);
                discountDetail.setSettleAmount(Objects.nonNull(subOrder.getDiscount()) ? subOrder.getDiscount() : 0);
                discountDetail.setCommissionAmount(ClearingComputeUtils.platformCommission(discountDetail.getSettleAmount(), ruleByMerchantCode));
                discountDetail.setMerchantAmount(discountDetail.getSettleAmount() - discountDetail.getCommissionAmount());
                discountDetail.setPayStatus("SC");
                discountDetail.setPayWay(BillTypeEnums.pay_channel_discount.getMsg());
                discountDetail.setCreatedDt(LocalDateTime.now());
                billDetailList.add(discountDetail);
            }
            // 结算规则
            bill.setSubFlowId(subOrder.getSubFlowId());
            bill.setSettleRule(ruleByMerchantCode.getSettleRuleCode());
            bill.setPlatformPercent(ruleByMerchantCode.getPlatformPre());
            bill.setSettleAmount(billDetailList.stream().mapToInt(BillDetail::getSettleAmount).sum());
            bill.setPlatformAmount(billDetailList.stream().mapToInt(BillDetail::getCommissionAmount).sum());
            bill.setMerchantAmount(bill.getSettleAmount() - bill.getPlatformAmount());
            billList.add(bill);
        }
        return transactionTemplate.execute(transactionStatus -> {
            try {
                saveBatch(billList);
                billDetailService.saveBatch(billDetailList);
                return true;
            } catch (Exception e) {
                transactionStatus.setRollbackOnly();
                throw e;
            }
        });
    }

    @Override
    public Boolean createAfterRefund(String refundId, List<MemberSubOrder> subOrders) {
        final List<MemberOrderRefund> refunds = getOrderRefundService().list(new LambdaQueryWrapper<MemberOrderRefund>().eq(MemberOrderRefund::getRefundId, refundId));
        if (CollectionUtils.isEmpty(refunds)) {
            log.warn("未查询到退款订单！退款单号：{}", refundId);
            return true;
        }

        final List<String> subOrderIds = refunds.stream().map(MemberOrderRefund::getSubOrderId).collect(Collectors.toList());
        final List<Bill> bills = this.list(new LambdaQueryWrapper<Bill>().in(Bill::getSubOrderId, subOrderIds));
        if (CollectionUtils.isEmpty(bills)) {
            return true;
        }
        // 已经生成过的退款账单的不处理
        refunds.removeIf(next -> bills.stream().anyMatch(b -> b.getBillType().equals(1) && b.getSubFlowId().equals(next.getSubRefundId()) && b.getSubOrderId().equals(next.getSubOrderId())));
        if (CollectionUtils.isEmpty(refunds)) {
            log.warn("没有需要再生成的退款账单");
            return true;
        }

        MemberOrder order = orderService.queryByOrderId(subOrders.get(0).getOrderId());

        final List<Bill> billList = new ArrayList<>();
        final List<BillDetail> billDetailList = new ArrayList<>();
        // 根据退款明细
        for (MemberOrderRefund refundOrdersDetail : refunds) {
            // 子订单
            MemberSubOrder subOrder = subOrders.stream().filter(s -> s.getSubOrderId().equals(refundOrdersDetail.getSubOrderId())).findFirst().get();

            // 子订单处理
            final Bill bill = new Bill();
            bill.setBillNo(orderIdHelper.getBillNo(refundOrdersDetail.getSubRefundId()));
            bill.setOrderId(refundOrdersDetail.getOrderId());
            bill.setSubOrderId(refundOrdersDetail.getSubOrderId());
            bill.setSubFlowId(refundOrdersDetail.getSubRefundId());
            bill.setTotalAmount(0);
            bill.setPayAmount(0);
            bill.setDiscount(0);
            bill.setRefundAmount(refundOrdersDetail.getRefundAmount());
            bill.setRefundDiscountAmount(refundOrdersDetail.getDiscount());
            bill.setSettleStatus(SettleStatusEnum.WAIT.getCode());
            bill.setVerificationStatus(VerificationStatusEnum.WAIT.getCode());
            bill.setBillRemark("");
            bill.setBillType(1);
            bill.setCreatedDt(LocalDateTime.now());
            // 获取清分规则
            final SettlementRuleResVO ruleByMerchantCode = settleRulesCache.getRuleByPlatformMerchantId(subOrder.getPlatformMerchantId());
            bill.setSettleRule("M" + ruleByMerchantCode.getSettleRuleCode());

            // 1、实付部分
            final BillDetail detail = new BillDetail();
            detail.setBillNo(bill.getBillNo());
            detail.setFlowId(refundOrdersDetail.getSubRefundId());
            detail.setChannelId(subOrder.getSubFlowId());
            detail.setBillType(BillTypeEnums.refund_pre.getCode());
            detail.setTotalAmount(refundOrdersDetail.getRefundApplyAmount());
            detail.setAmount(refundOrdersDetail.getRefundAmount());
            detail.setIsSettle(1);
            detail.setSettleAmount(negativeAmount(refundOrdersDetail.getRefundApplyAmount()));
            detail.setCommissionAmount(negativeAmount(ClearingComputeUtils.platformCommission(refundOrdersDetail.getRefundApplyAmount(), ruleByMerchantCode)));
            detail.setMerchantAmount(detail.getSettleAmount() - detail.getCommissionAmount());
            detail.setPayStatus("SC");
            detail.setPayWay(order.getPayWay());
            detail.setCreatedDt(LocalDateTime.now());
            billDetailList.add(detail);
            if (!refundOrdersDetail.getRefundAmount().equals(refundOrdersDetail.getRefundApplyAmount())) {
                // 2、优惠部分
                final BillDetail discountDetail = new BillDetail();
                discountDetail.setBillNo(bill.getBillNo());
                discountDetail.setFlowId(refundOrdersDetail.getSubRefundId());
                discountDetail.setChannelId(subOrder.getSubFlowId());
                discountDetail.setBillType(BillTypeEnums.refund_channel_discount.getCode());

                discountDetail.setTotalAmount(refundOrdersDetail.getDiscount());
                discountDetail.setAmount(refundOrdersDetail.getRefundApplyAmount() - refundOrdersDetail.getRefundAmount());
                discountDetail.setIsSettle(1);
                discountDetail.setSettleAmount(negativeAmount(discountDetail.getAmount()));
                discountDetail.setCommissionAmount(negativeAmount(ClearingComputeUtils.platformCommission(discountDetail.getAmount(), ruleByMerchantCode)));
                discountDetail.setMerchantAmount(discountDetail.getSettleAmount() + discountDetail.getCommissionAmount());
                discountDetail.setPayStatus("SC");
                discountDetail.setPayWay(BillTypeEnums.refund_channel_discount.getMsg());
                discountDetail.setCreatedDt(LocalDateTime.now());
                billDetailList.add(discountDetail);
            }

            // 结算规则
            bill.setRefundTotalAmount(billDetailList.stream().mapToInt(BillDetail::getTotalAmount).sum());
            bill.setRefundAmount(billDetailList.stream().mapToInt(BillDetail::getAmount).sum());
            bill.setRefundDiscountAmount(bill.getRefundTotalAmount() - bill.getRefundAmount());
            bill.setSettleRule(ruleByMerchantCode.getSettleRuleCode());
            bill.setPlatformPercent(ruleByMerchantCode.getPlatformPre());
            bill.setSettleAmount(billDetailList.stream().mapToInt(BillDetail::getSettleAmount).sum());
            bill.setPlatformAmount(billDetailList.stream().mapToInt(BillDetail::getCommissionAmount).sum());
            bill.setMerchantAmount(bill.getSettleAmount() - bill.getPlatformAmount());
            billList.add(bill);
        }
        return transactionTemplate.execute(transactionStatus -> {
            try {
                saveBatch(billList);
                billDetailService.saveBatch(billDetailList);
                return true;
            } catch (Exception e) {
                transactionStatus.setRollbackOnly();
                throw e;
            }
        });
    }

    /**
     * 重新计算账单信息
     *
     * @param bills 源账单信息
     */
    private void reComputeBill(List<Bill> bills) {
        final ArrayList<Bill> updateBills = new ArrayList<>();
        // 查询所有账单明细项
        List<BillDetail> billDetailsList = billDetailService.queryByBillNo(bills.stream().map(Bill::getBillNo).collect(Collectors.toList()));
        billDetailsList.stream().collect(Collectors.groupingBy(BillDetail::getBillNo)).forEach((billNo, bds) -> {
            Bill updateBill = new Bill();
            final Optional<Bill> first = bills.stream().filter(b -> b.getBillNo().equals(billNo)).findFirst();
            updateBill.setId(first.get().getId());
            updateBill.setRefundTotalAmount(bds.stream().filter(b -> b.getBillType() >= BillTypeEnums.refund_pre.getCode()).mapToInt(BillDetail::getTotalAmount).sum());
            updateBill.setRefundAmount(bds.stream().filter(b -> b.getBillType() >= BillTypeEnums.refund_pre.getCode()).mapToInt(BillDetail::getAmount).sum());
            updateBill.setRefundDiscountAmount(updateBill.getRefundTotalAmount() - updateBill.getRefundAmount());
            updateBill.setPlatformAmount(bds.stream().mapToInt(BillDetail::getCommissionAmount).sum());
            updateBill.setMerchantAmount(bds.stream().mapToInt(BillDetail::getMerchantAmount).sum());
            updateBill.setSettleAmount(updateBill.getPlatformAmount() + updateBill.getMerchantAmount());
            updateBills.add(updateBill);
        });
        this.updateBatchById(updateBills);
    }

    @Override
    public Boolean CompleteBill() {
        return null;
    }

    @Override
    public PageInfo<BillQueryRes> queryPage(QueryBillReq params) {
        IPage<Bill> page = new Page<>(params.getCurrentPage(), params.getPageSize());
        if (null != params.getBillDateBegin()) {
            params.setBillDateBegin(LocalDateTime.of(LocalDate.from(params.getBillDateBegin()), LocalDateTime.MIN.toLocalTime()));
        }
        if (null != params.getBillDateEnd()) {
            params.setBillDateEnd(LocalDateTime.of(LocalDate.from(params.getBillDateEnd()), LocalDateTime.MAX.toLocalTime()));
        }

        final IPage<BillQueryRes> pageResult = this.baseMapper.queryPage(page, params);
        return new PageInfo<>(pageResult.getCurrent(), pageResult.getSize(), pageResult.getTotal(), pageResult.getRecords());
    }

    @Override
    public TradeStatisticsRes tradeStatistics(Long platformMerchantId) {
        final TradeStatisticsRes tradeStatisticsRes = new TradeStatisticsRes();
        final Integer totalPayAmount = this.getOrderService().totalPayAmount(platformMerchantId);
        final Integer todayPayAmount = this.getOrderService().todayPayAmount(platformMerchantId);
        final CumulativeGainDTO cumulativeGainDTO = this.getBaseMapper().cumulativeGain(platformMerchantId);

        tradeStatisticsRes.setTotalPayAmount(Objects.nonNull(totalPayAmount) ? totalPayAmount : 0);
        tradeStatisticsRes.setTodayPayAmount(Objects.nonNull(todayPayAmount) ? todayPayAmount : 0);
        tradeStatisticsRes.setTotalSettleAmount(0);
        tradeStatisticsRes.setCumulativeGain(0);
        if (Objects.nonNull(cumulativeGainDTO)) {
            tradeStatisticsRes.setTotalSettleAmount(Objects.nonNull(cumulativeGainDTO.getTotalSettleAmount()) ? cumulativeGainDTO.getTotalSettleAmount() : 0);
            if(null!=platformMerchantId){
                // 查询的是商户收益
                tradeStatisticsRes.setCumulativeGain(Objects.nonNull(cumulativeGainDTO.getTotalMerchantAmount()) ? cumulativeGainDTO.getTotalMerchantAmount() : 0);
            }else{
                // 平台收益
                tradeStatisticsRes.setCumulativeGain(Objects.nonNull(cumulativeGainDTO.getTotalPlatformAmount()) ? cumulativeGainDTO.getTotalPlatformAmount() : 0);
            }

        }
        return tradeStatisticsRes;
    }

    @Override
    public List<StatisticsMerchantOrderPayRes> statisticsByDay(StatisticsMerchantOrderReq params) {
        return this.getBaseMapper().statisticsByDay(params);
    }

    @Override
    public boolean settleResultHandler(CallbackSettleMsgBO params, MessageTypeEnum msgType) {
        // 支付结算结果消息
        int billType = 0;
        if (msgType.equals(MessageTypeEnum.trade_payment_refund_settle_result)) {
            // 退款结算结果消息
            billType = 1;
        }
        final List<Bill> bills = this.list(new LambdaQueryWrapper<Bill>().eq(Bill::getSubOrderId, params.getSubOrderId()).eq(Bill::getSubFlowId, params.getSubFlowId()).eq(Bill::getBillType, billType));
        if (CollectionUtils.isEmpty(bills)) {
            return true;
        }

        Bill updateBill = new Bill();
        updateBill.setSettleStatus(params.getSettleStatus());
        updateBill.setMerchantAmount(params.getMerchantAmount());
        updateBill.setPlatformAmount(params.getPlatformAmount());
        updateBill.setPlatformPercent(params.getPlatformPercent());
        updateBill.setSettleDate(params.getSettleDate());
        updateBill.setSettleScDate(params.getSettleScDate());
        this.update(updateBill, new LambdaQueryWrapper<Bill>().in(Bill::getId, bills.stream().map(Bill::getId).toList()));
        if(msgType.equals(MessageTypeEnum.trade_payment_settle_result)){
            MemberSubOrder memberSubOrder = new MemberSubOrder();
            memberSubOrder.setSubOrderStatus(OrderStatusEnum.Completed.getCode());
            memberSubOrder.setLastUpdDt(LocalDateTime.now());
            subOrderService.update(memberSubOrder,new LambdaQueryWrapper<MemberSubOrder>().in(MemberSubOrder::getSubOrderId,bills.stream().map(Bill::getSubOrderId).collect(Collectors.toSet())));
        }
        return true;
    }

    private Integer negativeAmount(Integer sourceAmount) {
        return null == sourceAmount || sourceAmount == 0 ? 0 : -sourceAmount;
    }
}
